package com.acxy.pay;

import com.acxy.ants.common.kit.*;
import com.acxy.ants.core.Jetty;
import com.acxy.ants.core.annotation.Application;
import com.acxy.ants.core.annotation.Autowired;
import com.acxy.ants.core.config.AntsConfig;
import com.acxy.ants.core.config.Constants;
import com.acxy.ants.core.kit.LogKit;
import com.acxy.ants.resful.annotation.Controller;
import com.acxy.ants.resful.annotation.GET;
import com.acxy.ants.resful.annotation.POST;
import com.acxy.ants.resful.annotation.Param;
import com.acxy.ants.resful.render.Json;
import com.acxy.ants.resful.render.Resource;
import com.acxy.common.ApiResult;
import com.acxy.pay.weixin.*;
import com.alibaba.fastjson.JSON;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Map;
import java.util.TreeMap;

/**
 * 注意：1、网页授权必须在微信公众号里面配置授权目录
 * 2、支付必须要在商户里面配置支付授权路径
 *
 * @author MrShun
 * @version 1.0
 * @Date 2017-11-03
 */
@Controller("weixin")
@Application
public class App extends AntsConfig {

    @Autowired
    private PayService payService;

    @Autowired
    private OrderService orderService;

    /**
     * 【jsapi支付】
     * 微信jsapi支付
     */
    @GET("/jsapipay")
    public void jsApiPay(String code, HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (StrKit.notBlank(code)) {
            ApiResult result = payService.getOpenId(code);
            if (result.isOk()) {
                //金额
                BigDecimal total_money = new BigDecimal("0.01").multiply(new BigDecimal(100));
                BigInteger total_fee = (total_money).setScale(0, BigDecimal.ROUND_HALF_UP).toBigInteger();

                PayParams params = PayParams.newPayParams()
                        .setBody("测试JSAPI支付")
                        .setOpenId(result.get("openid"))
                        .setOutTradeNo("wx" + DateKit.getDataTime("yyyyMMddHHmmssSS"))
                        .setSpbillCreateIp(request.getRemoteAddr())
                        .setTotalFee(total_fee)
                        .setTradeType(PayType.JSAPI);

                ApiResult res = payService.getJsApiParams(params);
                if (res.isOk()) {
                    Resource resource = new Resource("static/pay.html", res);
                    resource.render(request, response);
                } else {
                    JSON.writeJSONStringTo(res, response.getWriter());
                }
            } else {
                LogKit.debug("errmsg > " + result.getMsg());
                String oauth2Url = payService.getOauth2Url(request, "snsapi_base");
                response.sendRedirect(oauth2Url);
            }
        } else {
            String oauth2Url = payService.getOauth2Url(request, "snsapi_base");
            response.sendRedirect(oauth2Url);
        }
    }

    /**
     * 【扫码支付模式1】
     * 微信扫码支付模式1, 需要在商户配置回调
     * 主要是先扫码，再生成订单
     */
    @GET("/code1pay")
    public void codeOnePay(HttpServletResponse response) throws IOException {
        PayParams params = PayParams.newPayParams()
                //商品ID, 可以通过URL传入
                .setProductId("acxy-000000001");
        String codeUrl = payService.makeScanCodeUrl(params);
        QRCodeKit.writeToOutputStream(codeUrl, response.getOutputStream());
    }

    /**
     * 微信扫码支付模式1的回调地址
     * 需要在商户>产品中心>开发配置进行设置
     */
    @POST("/code/notifyurl")
    public void scanCodeNotifyUrl(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Map notifyParams = payService.getNotify(request.getInputStream());
        //TODO 获取notifyParams主要参数
        Object openId = notifyParams.get("openid"); //用户ID
        Object productId = notifyParams.get("product_id"); //商品ID
        Object isSubscribe = notifyParams.get("is_subscribe"); //是否关注过公众号
        //TODO 根据上面商品ID查询相关商品信息, 完成业务处理
        //金额
        BigDecimal total_money = new BigDecimal("0.01").multiply(new BigDecimal(100));
        BigInteger total_fee = (total_money).setScale(0, BigDecimal.ROUND_HALF_UP).toBigInteger();

        PayParams params = PayParams.newPayParams()
                .setBody("测试扫描模式一支付")
                .setOutTradeNo("wx" + DateKit.getDataTime("yyyyMMddHHmmssSS"))
                .setSpbillCreateIp(request.getRemoteAddr())
                .setTotalFee(total_fee)
                .setTradeType(PayType.NATIVE);

        ApiResult result = payService.getUnifiedOrderParams(params);
        if (result.isOk()) {
            String res = MapXmlKit.map2Xml(new TreeMap(result), "xml");
            response.getWriter().write(res);
        } else {
            LogKit.debug("errmsg > " + result.getMsg());
            response.getWriter().write(JSON.toJSONString(result));
        }
    }

    /**
     * 【扫码支付模式2】
     * 微信扫码支付模式2, 预付单有效期为2小时，过期后无法支付
     * 主要是先生存订单, 在扫码完成支付
     */
    @GET("/code2pay")
    public void codeTwoPay(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //金额
        BigDecimal total_money = new BigDecimal("0.01").multiply(new BigDecimal(100));
        BigInteger total_fee = (total_money).setScale(0, BigDecimal.ROUND_HALF_UP).toBigInteger();

        PayParams params = PayParams.newPayParams()
                .setBody("测试扫描模式二支付")
                .setOutTradeNo("wx" + DateKit.getDataTime("yyyyMMddHHmmssSS"))
                .setSpbillCreateIp(request.getRemoteAddr())
                .setTotalFee(total_fee)
                .setTradeType(PayType.NATIVE);

        ApiResult result = payService.getUnifiedOrderParams(params);
        ServletOutputStream outputStream = response.getOutputStream();
        if (result.isOk()) {
            QRCodeKit.writeToOutputStream(result.getStr("code_url"), outputStream);
        } else {
            LogKit.debug("获取支付里面的code_url失败 > {}", result.getMsg());
            JSON.writeJSONStringTo(result, response.getWriter());
        }

    }

    /**
     * 【小程序支付】
     * 小程序支付, 必须在小程序后台配置https支付目录
     * 注意小程序appid, appsecret 和公众号的不同
     *
     * @param openid 用户ID
     */
    @POST("/smallpay")
    public Object smallPay(@Param String openid, HttpServletRequest request) {
        //金额
        BigDecimal total_money = new BigDecimal("0.01").multiply(new BigDecimal(100));
        BigInteger total_fee = (total_money).setScale(0, BigDecimal.ROUND_HALF_UP).toBigInteger();

        PayParams params = PayParams.newPayParams()
                .setOpenId(openid)
                .setBody("测试小程序支付")
                .setOutTradeNo("wx" + DateKit.getDataTime("yyyyMMddHHmmssSS"))
                .setSpbillCreateIp(request.getRemoteAddr())
                .setTotalFee(total_fee)
                .setTradeType(PayType.JSAPI);
        ApiResult result = payService.getUnifiedOrderParams(params);
        return result;
    }

    /**
     * 【App支付】
     * App内发起支付
     * 注意appid, 是申请App支付的id
     */
    @POST("/apppay")
    public Object appPay(HttpServletRequest request) {
        //金额
        BigDecimal total_money = new BigDecimal("0.01").multiply(new BigDecimal(100));
        BigInteger total_fee = (total_money).setScale(0, BigDecimal.ROUND_HALF_UP).toBigInteger();

        PayParams params = PayParams.newPayParams()
                .setBody("测试App支付")
                .setOutTradeNo("wx" + DateKit.getDataTime("yyyyMMddHHmmssSS"))
                .setSpbillCreateIp(request.getRemoteAddr())
                .setTotalFee(total_fee)
                .setTradeType(PayType.APP);
        ApiResult result = payService.getUnifiedOrderParams(params);
        return result;
    }

    /**
     * 【H5支付】
     * 需要到商户平台申请开通H5支付, 然后网页使用JS-SDK
     * 需要配置网页授权域
     */
    @GET("/h5pay")
    public Object h5Pay(HttpServletRequest request) {
        //金额
        BigDecimal total_money = new BigDecimal("0.01").multiply(new BigDecimal(100));
        BigInteger total_fee = (total_money).setScale(0, BigDecimal.ROUND_HALF_UP).toBigInteger();

        PayParams params = PayParams.newPayParams()
                .setBody("测试App支付")
                .setOutTradeNo("wx" + DateKit.getDataTime("yyyyMMddHHmmssSS"))
                .setSpbillCreateIp(request.getRemoteAddr())
                .setTotalFee(total_fee)
                .setTradeType(PayType.MWEB);
        ApiResult result = payService.getUnifiedOrderParams(params);
        return result;
    }

    /**
     * 支付回调通知地址, 必须是POST请求
     * 此处是jsapi支付和扫码模式二共用
     */
    @POST("/notifyurl")
    public void notifyUrl(HttpServletRequest request) throws IOException {
        LogKit.debug("weixin pay notify coming !");
        Map notify = payService.getNotify(request.getInputStream());
        if ("SUCCESS".equals(notify.get("result_code"))) {
            //TODO 收到成功通知, 处理回调业务
            LogKit.debug("notify data:{}", notify);
        } else {
            LogKit.debug("获取支付里面的code_url失败 > {}", notify);
        }
    }

    /**
     * 获取微信订单信息
     * @param id 微信订单ID
     * @return
     */
    @GET("/order/find")
    public Object find(@Param String id, @Param int type){
        return orderService.getWxOrder(id, type);
    }

    /**
     * 申请退款, 需要双向证书
     * @param id 商户订单ID
     * @param total_fee 总金额
     * @param out_refund_no 退款单号
     * @param refund_fee 退款金额
     * @return
     */
    @GET("/order/refund")
    public Object refund(@Param String id, @Param String total_fee, @Param String out_refund_no, @Param String refund_fee){
        return orderService.refund(id, total_fee, out_refund_no, refund_fee);
    }

    /**
     * 下载对账单
     * @param date 微信订单ID
     * @return
     */
    @GET("/order/down")
    public Object down(@Param String date, @Param String type){
        Object result = orderService.downLoadBill(date, type);
        //TODO 处理对账账单业务逻辑 可以存储到本地数据库
        if(String.valueOf(result).indexOf("return_code") == -1){
            return result;
        }else{
            return MapXmlKit.xml2Map(String.valueOf(result), "xml");
        }
    }

    /**
     * 拉取订单评价数据
     * @param offset 指定某条记录
     * @param limit 每页大小
     * @param begin 开始时间 格式为yyyyMMddHHmmss
     * @param end 开始时间 格式为yyyyMMddHHmmss
     * @return
     */
    @GET("/order/comment")
    public Object comment(@Param int offset, @Param int limit, @Param String begin, @Param String end){
        return orderService.batchQueryComment(offset, limit, begin, end);
    }

    //微信退款通知地址
    @POST("/refund/notifyurl")
    public void refundNotifyUrl(HttpServletRequest request) throws Exception {
        LogKit.debug("weixin refund notify coming !");
        Map result = orderService.refundNotifyUrl(request.getInputStream());
        //TODO 处理退款通知记录操作
        LogKit.debug("退款通知结果 > {}", JSON.toJSONString(result));
    }

    @Override
    public void configConstant(Constants me) {
        PropKit.use("weixin.properties");
        me.setDebug(true);
    }

    public static void main(String[] args) {
        Jetty.start(80, "/");
    }
}
